// -*- c-basic-offset: 2 default-tab-width: 2 indent-tabs-mode: t -*-
// vim: autoindent tabstop=2 noexpandtab shiftwidth=2 softtabstop=2
#include "util.h"
#include "BuiltIns.h"
#include "Sequence.h"
#include "Arithmetic.h"
#include "Const.h"
#include "RAttributes.h"
#include "ValueBox.h"

using namespace R;

Any *invalidBuiltIn(uint32_t nbArgs, Any **args){
	rfatal("Invalid BuiltIn");
	// FIXME when we'll throw an exception, we'll have to pop
	return ConstPool::Null; // to keep everybody happy
}

Any* getAttributes(uint32_t nbArgs, Any **args){
	R_VAL(Any*, left_op, args[0]);

	if(left_op == ConstPool::Null)
		return ConstPool::Null;

	return left_op->attributes;
}

Any* getAttr(uint32_t nbArgs, Any** args){
	R_VAL(Any*, left_op, args[0]);
	R_VAL(Any*, attr_name, args[1]);

	if((left_op = RAttributes::checkGetAttr(left_op, attr_name)) == ConstPool::Null)
		return ConstPool::Null;

	return left_op->getAttr(static_cast<String*>(attr_name)->getElement(0));
}

Any* setAttr(uint32_t nbArgs, Any **args){
	R_VAL(Any*, left_op, args[0]);
	R_VAL(Any*, right_op, args[1]);
	R_VAL(Any*, attr_name, args[2]);

	// First check if receiver is null
	rerror_on(left_op == ConstPool::Null, "invalid (NULL) left side of assignment");

	// here check attribute and fix right_op if needed
	right_op = RAttributes::checkSetAttr(left_op, attr_name, right_op);

	// Finally set the attribute and return corrected right_op
	return left_op->setAttr(static_cast<String*>(attr_name)->getElement(0), right_op);
}

Any *setAttributes(uint32_t nbArgs, Any** args){
	R_VAL(Any*, left_op, args[0]);
	R_VAL(Any*, right_op, args[1]);

	// First check if receiver is null
	rerror_on(left_op == ConstPool::Null, "invalid (NULL) left side of assignment");

	// Check and correct attributes
	right_op = RAttributes::checkSetAttributes(left_op, right_op);

	// Finally set the attribute.
	left_op->setAttributes(right_op);

	// return modified right_op
	return right_op;
}

Any *reverse(uint32_t nbArgs, Any **args){
	return static_cast<Any*>(args[0]->reverse());
}

Any *length(uint32_t nbArgs, Any **args){
	R_VAL(Any*, self, args[0]);
	rerror_on(!Any::isOfType<Array>(self), "Length is only valid on arrays !");
	return Int::doNewSingle(static_cast<Array*>(self)->length);
}

Any *bmax(uint32_t nbArgs, Any** args){
	R_VAL(Any*, first, args[0]);
	R_VAL(Any*, second, args[1]);
	rerror_on(!Any::isOfType<Array>(first), "'max' is only valid on arrays !");
	rerror_on(!Any::isOfType<Array>(second), "'max' is only valid on arrays !");
	switch(Any::getType(first) >> SXP_LOW_PART){
		case Double::ID >> SXP_LOW_PART:{
			double f = static_cast<Double*>(first)->getElement(0);
			double s = static_cast<Double*>(second)->getElement(0);
			return Double::doNewSingle(max(f, s));
			}break;
		default:
			nyi_fatal();
	}
	return ConstPool::Null;
}

Any *bmin(uint32_t nbArgs, Any** args){
	R_VAR(Any *, array);
	ValueBox box;

	while(nbArgs --){
		array = args[nbArgs];
		TYPECASE(Any::getType(array),
				Logical(
					if(!box.has<r_bool>(0)) box.set(static_cast<Logical*>(array)->max());
					else box.set(max(box.get<r_bool>(), static_cast<Logical*>(array)->max()))),
				Int(
					if(!box.has<r_int>(0)) box.set(static_cast<Int*>(array)->max());
					else box.set(max(box.get<r_int>(), static_cast<Int*>(array)->max()))),
				Double(
					if(!box.has<r_double>(0)) box.set(static_cast<Double*>(array)->max());
					else box.set(max(box.get<r_double>(), static_cast<Double*>(array)->max()))),
				Default(nyi_fatal())
				)
	}
	if(box.has<r_bool>(0)){
		if(box.has<r_int>(0))
			box.set(max(box.get<r_int>(), convert<r_bool, r_int>(box.get<r_bool>())));
		else if(box.has<r_double>(0))
			box.set(max(box.get<r_double>(), convert<r_bool, r_double>(box.get<r_bool>())));
		else if(box.has<r_string>(0))
			box.set(max(box.get<r_string>(), convert<r_bool, r_string>(box.get<r_bool>())));
		return Logical::doNewSingle(box.get<r_bool>());
	}
	if(box.has<r_int>(0)){
		if(box.has<r_double>(0))
			box.set(max(box.get<r_double>(), convert<r_int, r_double>(box.get<r_int>())));
		else if(box.has<r_string>(0))
			box.set(max(box.get<r_string>(), convert<r_int, r_string>(box.get<r_int>())));
		return Int::doNewSingle(box.get<r_int>());
	}
	if(box.has<r_double>(0)){
		if(box.has<r_string>(0))
			box.set(max(box.get<r_string>(), convert<r_double, r_string>(box.get<r_double>())));
		return Double::doNewSingle(box.get<r_double>());
	}
	return String::doNewSingle(box.get<r_string>());
}

Any *column(uint32_t nbArgs, Any** args){
	R_VAL(Any*, r, args[1]);
	R_VAL(Any*, l, args[0]);
	return columnOperator(l, r);
}

Any *vector(uint32_t nbArgs, Any** args){
	R_VAL(Any *, type, args[0]);
	R_VAL(Any *, len, args[1]);
	// FIXME use the good type
	nyi();
	return Double::doNew(len->toIntOne());
}

Any *list(uint32_t nbArgs, Any** args){
	R_VAL(Vector*, self, Vector::doNew(nbArgs));
	while(nbArgs --)
		self->content[nbArgs] = args[nbArgs]->duplicate();
	/*printf("[LIST]\t");
	self->print();
	PrintNewLine();*/
	return self;
}

#define FILL_IS_NA(__type, theNA) \
while(len --)\
	self->content[len] = (static_cast<__type*>(arg)->content[len] == theNA) ? 1 : 0; /*TODO replace this *** numbers by some consts*/

Any *isna(uint32_t nbArgs, Any** args){
	R_VAR(Logical *, self);
	R_VAL(Array *, arg, static_cast<Array*>(args[0])); // Note that in non debug mode this will never trigger an error !
	int t = Any::getType(arg);

	rerror_on(!Any::isOfType<Array>(t), "is.na() applied to non vector object !");
	int len = arg->length;
	self = Logical::doNew(len);

	TYPECASE(t,
			Logical(FILL_IS_NA(Logical, ConstPool::NA_LOG)),
			Int(FILL_IS_NA(Int, ConstPool::NA_INT)),
			Double(FILL_IS_NA(Double, ConstPool::NA_DOUBLE)),
			String(FILL_IS_NA(String, ConstPool::NA_STRING)),
			Vector(nyi_fatal()));
	return  self;
}
#undef FILL_IS_NA

Any *combine(uint32_t nbArgs, Any** args){
	R_VAR(Any *, argi);
	int i = nbArgs;
	int vectorItems = 0;
	int maxType = 0;

	while(i --){
		argi = args[i];
		int t = Any::getType(argi);
		if(Any::isOfType<Array>(t)){
			vectorItems	+= static_cast<Array*>(argi)->length;
			if(t > maxType)
				maxType = t;
		} else {
			vectorItems ++;
			if(maxType != Vector::ID) maxType = Vector::ID;
		}
	}

	TYPECASE(maxType,
				Logical(argi = Logical::combine(vectorItems, nbArgs, args)),
				Int(argi = Int::combine(vectorItems, nbArgs, args)),
				Double(argi = Double::combine(vectorItems, nbArgs, args)),
				String(argi = String::combine(vectorItems, nbArgs, args)),
				Vector(argi = Vector::combine(vectorItems, nbArgs, args)),
				Default(nyi_fatal()))

	return argi;
}

Any *missing(EnvInfo *env, uint32_t nbArgs, Any** args){
	rerror_on(!Any::isOfType<Symbol>(*args), "'missing' only works for symbol"); // FIXME is it an assert or an error ??? Maybe the compiler has to check for type earlier
	return env->exists(static_cast<Symbol*>(*args)) ? ConstPool::False : ConstPool::True;
}

BuiltInMap map[] = {
	BuiltInMap("", 0, &invalidBuiltIn),
	BuiltInMap(":", 2, &column),
	BuiltInMap("length", 1, &length),
	BuiltInMap("max", 2, &bmax), // TODO -1
	BuiltInMap("min", -1, &bmin),
	BuiltInMap("vector", 2, &vector),
	BuiltInMap("rev.default", 1, &reverse),
	BuiltInMap("attr<-", 3, &setAttr),
	BuiltInMap("attr", 2, &getAttr),
	BuiltInMap("attributes<-", 2, &setAttributes),
	BuiltInMap("attributes", 1, &getAttributes),
	BuiltInMap("c", -1, &combine),
	BuiltInMap("list", -1, &list),
	BuiltInMap("is.na", 1, &isna),
	BuiltInMap("missing", 1, &missing),
	BuiltInMap("ASTUPIDNAME", 0, &invalidBuiltIn) // This sentinel is not needed, but it's easyier to do copy/paste
};
int BuiltInMap::maplen = (sizeof(map)/sizeof(BuiltInMap));

BuiltInMap *BuiltInMap::getEntry(char *name){
	return getEntry(getEntryID(name));
}

BuiltInMap *BuiltInMap::getEntry(int id){
	Assert(isValidEntry(id), "Invalid builtin entry id: %d", id);
	return &map[id];
}

int BuiltInMap::getEntryID(const char *name){
	int id = maplen;
	while(--id)
		if(!strcmp(name, map[id].name))
			return id;
	return 0;
}

Any* BuiltInMap::callBuiltIn(uint32_t nbArgs, Any **args){
	R_VAR(Any*, res);	
	Assert(!isSpecial, "This is a 'special', but is called like a 'builtin'!");
	Assert(nbArgs == nbParams || nbArgs > ( -nbParams - 1), "Builtin has not enough arguments, %d required but only %d passed", -nbParams + 1, nbArgs);

	return res = builtin(nbArgs, args);
}

Any* BuiltInMap::callSpecial(uint32_t nbArgs, Any **args){
	R_VAR(Any*, res);	
	Assert(isSpecial, "This is a 'builtin', but is called like a 'special'!");
	Assert(nbArgs == nbParams || nbArgs > ( -nbParams - 1), "Special has not enough arguments, %d required but only %d passed", -nbParams + 1, nbArgs);

	return res = special(Evaluator::get()->envInfo, nbArgs, args);
}
